﻿// -----------------------------------------------------------------------
// <copyright file="LocatorInstanceProvider.cs" company="Henadzi Sabaleuski.">
// Copyright (c) Henadzi Sabaleuski. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

namespace OCM.Enterprise.OCR.Utilities.Unity
{
	#region Usings

	using System;
	using System.ServiceModel;
	using System.ServiceModel.Channels;
	using System.ServiceModel.Dispatcher;
	using HSabaleuski.Utilities.Diagnostics;
	using Microsoft.Practices.ServiceLocation;
	using Microsoft.Practices.Unity;

	#endregion

	/// <summary>
	/// The <see cref="IInstanceProvider"/> implementer which allows to create and recycle service instances
	/// using Service Locator and Unity container.
	/// </summary>
	public class LocatorInstanceProvider : IInstanceProvider
	{
		#region Private Fields

		/// <summary>
		/// The <see cref="IServiceLocator"/> field which used for Dependency Injection purposes.
		/// </summary>
		private readonly IServiceLocator serviceLocator;

		/// <summary>
		/// The service type for instantiation and dependency resolving.
		/// </summary>
		private readonly Type serviceType;

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="LocatorInstanceProvider"/> class.
		/// </summary>
		/// <param name="serviceLocator">The service locator.</param>
		/// <param name="serviceType">Type of the service.</param>
		public LocatorInstanceProvider(IServiceLocator serviceLocator, Type serviceType)
		{
			Guard.VerifyNullable(serviceLocator);
			Guard.VerifyNullable(serviceType);

			this.serviceLocator = serviceLocator;
			this.serviceType = serviceType;
		}

		#endregion

		#region IInstanceProvider Members

		/// <summary>
		/// Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
		/// </summary>
		/// <param name="instanceContext">The current <see cref="T:System.ServiceModel.InstanceContext"/> object.</param>
		/// <returns>A user-defined service object.</returns>
		public object GetInstance(InstanceContext instanceContext)
		{
			return this.GetInstance(instanceContext, null);
		}

		/// <summary>
		/// Returns a service object given the specified <see cref="T:System.ServiceModel.InstanceContext"/> object.
		/// </summary>
		/// <param name="instanceContext">The current <see cref="T:System.ServiceModel.InstanceContext"/> object.</param>
		/// <param name="message">The message that triggered the creation of a service object.</param>
		/// <returns>The service object.</returns>
		public object GetInstance(InstanceContext instanceContext, Message message)
		{
			if (instanceContext == null)
			{
				throw new ArgumentNullException("instanceContext");
			}

			var instance = this.serviceLocator.GetInstance(this.serviceType);

			return instance;
		}

		/// <summary>
		/// Called when an <see cref="T:System.ServiceModel.InstanceContext"/> object recycles a service object.
		/// </summary>
		/// <param name="instanceContext">The service's instance context.</param>
		/// <param name="instance">The service object to be recycled.</param>
		public void ReleaseInstance(InstanceContext instanceContext, object instance)
		{
			IUnityContainer container = this.serviceLocator.GetInstance<IUnityContainer>();
			if (container != null)
			{
				// If container is not null then call Tear Down for instance.
				container.Teardown(instance);
			}
			else
			{
				// If container is null, check whether instance implement IDisposable or not.
				// If so, call Dispose of the instance.
				IDisposable disposableInstance = instance as IDisposable;
				if (disposableInstance != null)
				{
					disposableInstance.Dispose();
				}
			}
		}

		#endregion
	}
}